cmake_minimum_required(VERSION 3.20)

# Option to use Conan for dependency management
# Check if we're running with a Conan preset (the toolchain file will be in generators/)
if(CMAKE_TOOLCHAIN_FILE MATCHES "conan_toolchain\\.cmake$")
    set(YAMS_USE_CONAN ON CACHE BOOL "Using Conan package manager" FORCE)
else()
    option(YAMS_USE_CONAN "Use Conan package manager for dependencies" OFF)
endif()

# Derive version from git tags or allow override via -DYAMS_VERSION=MAJOR.MINOR.PATCH
set(YAMS_VERSION "" CACHE STRING "Override Yams version (semver: MAJOR.MINOR.PATCH)")

if(NOT YAMS_VERSION)
    find_package(Git QUIET)
    if(Git_FOUND)
        execute_process(
            COMMAND "${GIT_EXECUTABLE}" describe --tags --match "v[0-9]*" --dirty --always
            WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
            OUTPUT_VARIABLE GIT_DESCRIBE
            OUTPUT_STRIP_TRAILING_WHITESPACE
        )
        # Strip leading 'v' and any suffix like -123-gabcdef
        string(REGEX REPLACE "^v" "" YAMS_VERSION_CANDIDATE "${GIT_DESCRIBE}")
        string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" YAMS_SEMVER "${YAMS_VERSION_CANDIDATE}")
        if(NOT YAMS_SEMVER)
            set(YAMS_SEMVER "0.0.0")
        endif()
    else()
        set(YAMS_SEMVER "0.0.0")
    endif()
else()
    set(YAMS_SEMVER "${YAMS_VERSION}")
endif()

project(YAMS
    VERSION ${YAMS_SEMVER}
    DESCRIPTION "Yet Another Memory System - High-performance content-addressed storage"
    LANGUAGES C CXX
)

# Generate version header from project version
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated/yams")

# Write template for version header and configure it
set(_YAMS_VERSION_TEMPLATE "${CMAKE_CURRENT_BINARY_DIR}/yams_version.hpp.in")
file(WRITE "${_YAMS_VERSION_TEMPLATE}" "/* Auto-generated by CMake - Do not edit */
#pragma once

#define YAMS_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define YAMS_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define YAMS_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define YAMS_VERSION_STRING \"@PROJECT_VERSION@\"
")

configure_file(
    "${_YAMS_VERSION_TEMPLATE}"
    "${CMAKE_CURRENT_BINARY_DIR}/generated/yams/version.hpp"
    @ONLY
)

# Ensure default CMake package dir uses 'Yams' casing for consumers
include(GNUInstallDirs)

# Install generated version header
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/generated/yams/version.hpp"
        DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/yams"
        COMPONENT development)
set(YAMS_CMAKE_PACKAGE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/Yams"
    CACHE PATH "Install dir for YAMS CMake package files")

# Modern CMake policies (set only if available)
if(POLICY CMP0077) # option() honors normal variables
    cmake_policy(SET CMP0077 NEW)
endif()
if(POLICY CMP0126) # file(GENERATE) supports generator expressions in OUTPUT
    cmake_policy(SET CMP0126 NEW)
endif()
if(POLICY CMP0135) # IN_LIST behavior and other modern improvements
    cmake_policy(SET CMP0135 NEW)
endif()

# C++ standard configuration
option(YAMS_CXX17_MODE "Use C++17 for broader compatibility (default: C++20)" OFF)

if(YAMS_CXX17_MODE)
    set(CMAKE_CXX_STANDARD 17)
    message(STATUS "Using C++17 compatibility mode")
    
    # Compiler version checks for C++17 mode
    if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
        if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9)
            message(FATAL_ERROR "GCC ${CMAKE_CXX_COMPILER_VERSION} detected. GCC 9+ is required for C++17 support.")
        endif()
        message(STATUS "Using GCC ${CMAKE_CXX_COMPILER_VERSION} in C++17 mode")
    elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
        if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9)
            message(FATAL_ERROR "Clang ${CMAKE_CXX_COMPILER_VERSION} detected. Clang 9+ is required for C++17 support.")
        endif()
        message(STATUS "Using Clang ${CMAKE_CXX_COMPILER_VERSION} in C++17 mode")
    endif()
else()
    set(CMAKE_CXX_STANDARD 20)
    message(STATUS "Using C++20 mode (default)")
    
    # Compiler version checks for C++20 mode
    if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
        if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11)
            message(FATAL_ERROR "GCC ${CMAKE_CXX_COMPILER_VERSION} detected. GCC 11+ is required for C++20 support.")
        endif()
        message(STATUS "Using GCC ${CMAKE_CXX_COMPILER_VERSION} in C++20 mode")
    elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
        if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12)
            message(FATAL_ERROR "Clang ${CMAKE_CXX_COMPILER_VERSION} detected. Clang 12+ is required for C++20 support.")
        endif()
        message(STATUS "Using Clang ${CMAKE_CXX_COMPILER_VERSION} in C++20 mode")
    endif()
endif()

set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Check for std::format availability (only in C++20 mode)
include(CheckCXXSourceCompiles)
if(NOT YAMS_CXX17_MODE)
    check_cxx_source_compiles("
        #include <version>
        #ifdef __cpp_lib_format
        #include <format>
        int main() {
            auto s = std::format(\"{}\", 42);
            return 0;
        }
        #else
        #error No format support
        #endif
    " YAMS_HAS_STD_FORMAT)
else()
    # In C++17 mode, std::format is not available
    set(YAMS_HAS_STD_FORMAT FALSE)
    message(STATUS "C++17 mode: std::format not available, using fmt fallback")
endif()

if(YAMS_HAS_STD_FORMAT)
    add_compile_definitions(YAMS_HAS_STD_FORMAT=1)
    message(STATUS "std::format is available")
else()
    add_compile_definitions(YAMS_HAS_STD_FORMAT=0)
    message(STATUS "std::format is NOT available - using fmt fallback")
endif()

# Export compile commands for tooling
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Build profile options
set(YAMS_BUILD_PROFILE "custom" CACHE STRING "Build profile: release, dev, or custom")
set_property(CACHE YAMS_BUILD_PROFILE PROPERTY STRINGS release dev custom)

# Individual component options
option(YAMS_BUILD_CLI "Build CLI tool" ON)
option(YAMS_BUILD_MCP_SERVER "Build MCP server" ON)
option(YAMS_BUILD_MAINTENANCE_TOOLS "Build maintenance tools (gc, stats)" OFF)
option(YAMS_BUILD_TESTS "Build unit tests" OFF)
option(YAMS_BUILD_STRESS_TESTS "Build stress tests" OFF)
option(YAMS_BUILD_BENCHMARKS "Build benchmarks" OFF)
option(YAMS_ENABLE_SANITIZERS "Enable sanitizers in Debug" ON)
option(YAMS_ENABLE_COVERAGE "Enable code coverage" OFF)
option(YAMS_USE_VCPKG "Use vcpkg for dependencies" OFF)
option(YAMS_ENABLE_NATIVE_OPTIMIZATIONS "Enable -march=native on supported platforms" OFF)
option(YAMS_ENABLE_APPLE_SILICON_OPTIMIZATIONS "Enable Apple Silicon optimizations (auto-enabled on ARM64 Macs)" ON)
option(YAMS_CLANG_USE_LIBCXX "Use libc++ with Clang (Linux requires libc++ dev packages)" OFF)
option(YAMS_BUILD_API_DOCS "Build Doxygen API documentation" OFF)

# ------------------------------------------------------------------------------
# Docs build (Pandoc) and embedded CLI help assets
# ------------------------------------------------------------------------------
option(YAMS_BUILD_DOCS "Build CLI docs, generate manpages, and embed verbose help" OFF)
find_program(PANDOC_EXECUTABLE pandoc)

set(CLI_MD "${CMAKE_SOURCE_DIR}/docs/user_guide/cli.md")
set(GEN_DIR "${CMAKE_BINARY_DIR}/generated/cli")
set(MAN_DIR "${CMAKE_BINARY_DIR}/man")

if(YAMS_BUILD_DOCS AND PANDOC_EXECUTABLE AND EXISTS "${CLI_MD}")
    # Ensure output directories
    file(MAKE_DIRECTORY "${GEN_DIR}")
    file(MAKE_DIRECTORY "${MAN_DIR}")
    file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/generated")
    file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/cmake")

    # 1) Generate full plain-text help from Markdown
    add_custom_command(
        OUTPUT "${GEN_DIR}/cli_help.txt"
        COMMAND "${PANDOC_EXECUTABLE}" -f markdown -t plain "${CLI_MD}" -o "${GEN_DIR}/cli_help.txt"
        DEPENDS "${CLI_MD}"
        COMMENT "Generating plain-text CLI help from Markdown"
        VERBATIM
    )

    # 2) Generate yams(1) manpage from Markdown
    add_custom_command(
        OUTPUT "${MAN_DIR}/yams.1"
        COMMAND "${PANDOC_EXECUTABLE}" -s -f markdown -t man "${CLI_MD}" -o "${MAN_DIR}/yams.1"
        DEPENDS "${CLI_MD}"
        COMMENT "Generating yams(1) manpage from Markdown"
        VERBATIM
    )

    # 3) Embed script (converts a text file to a C++ header with a constexpr string)
    set(EMBED_SCRIPT "${CMAKE_BINARY_DIR}/cmake/embed_text.cmake")
    file(WRITE "${EMBED_SCRIPT}"
"if(NOT DEFINED INPUT)
  message(FATAL_ERROR \"INPUT not set\")
endif()
if(NOT DEFINED OUTPUT)
  message(FATAL_ERROR \"OUTPUT not set\")
endif()

# Optional: caller can pass -DSYMBOL=<name>. Defaults to VERBOSE.
if(NOT DEFINED SYMBOL)
  set(SYMBOL \"VERBOSE\")
endif()
set(_SYM \"\${SYMBOL}\")
string(TOUPPER \"\${_SYM}\" _SYM)
string(REGEX REPLACE \"[^A-Za-z0-9_]\" \"_\" _SYM \"\${_SYM}\")
# Prefix command-specific symbols for clarity (keep full doc as VERBOSE)
if(NOT _SYM STREQUAL \"VERBOSE\")
  set(_SYM \"CMD_\${_SYM}\")
endif()

# Strip quotes from INPUT and OUTPUT if present
string(REGEX REPLACE \"^\\\"(.*)\\\"$\" \"\\\\1\" INPUT_CLEAN \"\${INPUT}\")
string(REGEX REPLACE \"^\\\"(.*)\\\"$\" \"\\\\1\" OUTPUT_CLEAN \"\${OUTPUT}\")

# Check if input file exists
if(NOT EXISTS \"\${INPUT_CLEAN}\")
  message(FATAL_ERROR \"Input file does not exist: '\${INPUT_CLEAN}'\")
endif()

# Check if output directory exists
get_filename_component(OUTPUT_DIR \"\${OUTPUT_CLEAN}\" DIRECTORY)
if(NOT EXISTS \"\${OUTPUT_DIR}\")
  file(MAKE_DIRECTORY \"\${OUTPUT_DIR}\")
endif()

file(READ \"\${INPUT_CLEAN}\" _c)
string(REPLACE \"\\\\\" \"\\\\\\\\\" _c \"\${_c}\")
string(REPLACE \"\\\"\" \"\\\\\\\"\" _c \"\${_c}\")
string(REPLACE \"\\n\" \"\\\\n\\\"\\n\\\"\" _c \"\${_c}\")

file(WRITE \"\${OUTPUT_CLEAN}\" \"// Generated by embed_text.cmake - do not edit\\n#pragma once\\nnamespace yams { namespace cli_help {\\nconstexpr const char \${_SYM}[] = \\\"\${_c}\\\";\\n} } // namespace yams::cli_help\\n\")
")

    # 4) Embed full verbose help into a header
    add_custom_command(
        OUTPUT "${CMAKE_BINARY_DIR}/generated/cli_help.hpp"
        COMMAND "${CMAKE_COMMAND}" -DINPUT="${GEN_DIR}/cli_help.txt" -DOUTPUT="${CMAKE_BINARY_DIR}/generated/cli_help.hpp" -DSYMBOL=VERBOSE -P "${EMBED_SCRIPT}"
        DEPENDS "${GEN_DIR}/cli_help.txt" "${EMBED_SCRIPT}"
        COMMENT "Embedding full CLI help into header"
        VERBATIM
    )

    # 5) Split per-command sections from the Markdown for detailed command help
    #    The splitter scans lines and collects sections for known commands.
    set(SPLIT_SCRIPT "${CMAKE_BINARY_DIR}/cmake/split_cli_sections.cmake")
    file(WRITE "${SPLIT_SCRIPT}"
"set(CLI_MD \"${CLI_MD}\")
set(OUT_DIR \"${GEN_DIR}\")
# Commands to extract (must match headings like '## <name>' or '## <name> {#cmd-name}')
set(COMMANDS init add get delete list search config auth stats uninstall migrate browse serve)

file(STRINGS \"${CLI_MD}\" LINES)
set(current \"\")
set(in_section FALSE)
set(content \"\")

function(_flush_section name)
  if(NOT \"\${name}\" STREQUAL \"\")
    set(path \"\${OUT_DIR}/cli_help_\${name}.txt\")
    file(WRITE \"\${OUT_DIR}/tmp_\${name}.txt\" \"\${content}\")
    file(RENAME \"\${OUT_DIR}/tmp_\${name}.txt\" \"\${path}\")
  endif()
endfunction()

foreach(line IN LISTS LINES)
  if(line MATCHES \"^##[ ]+([A-Za-z0-9_-]+)[ ]*(\\{#cmd-.*\\})?\")
    string(REGEX REPLACE \"^##[ ]+([A-Za-z0-9_-]+).*$\" \"\\\\1\" heading \"\${line}\")
    # Close previous section if any
    if(in_section)
      _flush_section(\"\${current}\")
      set(content \"\")
    endif()
    # Start a new section only if the heading is one of our commands
    list(FIND COMMANDS \"\${heading}\" idx)
    if(NOT idx EQUAL -1)
      set(current \"\${heading}\")
      set(in_section TRUE)
      set(content \"\${line}\\n\")
    else()
      set(current \"\")
      set(in_section FALSE)
      set(content \"\")
    endif()
  else()
    if(in_section)
      set(content \"\${content}\${line}\\n\")
    endif()
  endif()
endforeach()

# Flush the last open section
if(in_section)
  _flush_section(\"\${current}\")
endif()
")

    add_custom_command(
        OUTPUT
            "${GEN_DIR}/cli_help_init.txt"
            "${GEN_DIR}/cli_help_add.txt"
            "${GEN_DIR}/cli_help_get.txt"
            "${GEN_DIR}/cli_help_delete.txt"
            "${GEN_DIR}/cli_help_list.txt"
            "${GEN_DIR}/cli_help_search.txt"
            "${GEN_DIR}/cli_help_config.txt"
            "${GEN_DIR}/cli_help_auth.txt"
            "${GEN_DIR}/cli_help_stats.txt"
            "${GEN_DIR}/cli_help_uninstall.txt"
            "${GEN_DIR}/cli_help_migrate.txt"
            "${GEN_DIR}/cli_help_browse.txt"
            "${GEN_DIR}/cli_help_serve.txt"
        COMMAND "${CMAKE_COMMAND}" -P "${SPLIT_SCRIPT}"
        DEPENDS "${CLI_MD}" "${SPLIT_SCRIPT}"
        COMMENT "Splitting per-command CLI sections from Markdown"
        VERBATIM
    )

    # 6) Embed per-command verbose help headers
    set(EMBED_HEADERS)
    foreach(cmd init add get delete list search config auth stats uninstall migrate browse serve)
        set(infile "${GEN_DIR}/cli_help_${cmd}.txt")
        set(outfile "${CMAKE_BINARY_DIR}/generated/cli_help_${cmd}.hpp")
        add_custom_command(
            OUTPUT "${outfile}"
            COMMAND "${CMAKE_COMMAND}" -DINPUT="${infile}" -DOUTPUT="${outfile}" -DSYMBOL=${cmd} -P "${EMBED_SCRIPT}"
            DEPENDS "${infile}" "${EMBED_SCRIPT}"
            COMMENT "Embedding ${cmd} CLI help into header"
            VERBATIM
        )
        list(APPEND EMBED_HEADERS "${outfile}")
    endforeach()

    # 7) Aggregate docs target
    add_custom_target(docs
        DEPENDS
            "${CMAKE_BINARY_DIR}/generated/cli_help.hpp"
            ${EMBED_HEADERS}
            "${MAN_DIR}/yams.1"
    )

    # 8) Install generated manpage
    include(GNUInstallDirs)
    install(FILES "${MAN_DIR}/yams.1" DESTINATION "${CMAKE_INSTALL_MANDIR}/man1" COMPONENT runtime)

    # 9) Install data files (magic_numbers.json)
    install(FILES "${CMAKE_SOURCE_DIR}/data/magic_numbers.json" 
            DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/yams/data" 
            COMPONENT runtime)

    # 10) Global compile definition to indicate embedded help is available
    add_compile_definitions(YAMS_EMBEDDED_VERBOSE_HELP=1)

else()
    if(YAMS_BUILD_DOCS AND NOT PANDOC_EXECUTABLE)
        message(WARNING "YAMS_BUILD_DOCS=ON but Pandoc was not found. Install Pandoc and re-configure CMake.
- macOS: brew install pandoc
- Ubuntu/Debian: sudo apt-get update && sudo apt-get install -y pandoc
- Fedora: sudo dnf install -y pandoc
- Arch: sudo pacman -S pandoc
- Windows (PowerShell): choco install pandoc    or    scoop install pandoc")
    elseif(YAMS_BUILD_DOCS AND NOT EXISTS "${CLI_MD}")
        message(WARNING "YAMS_BUILD_DOCS=ON but CLI markdown not found at ${CLI_MD}; skipping docs build.")
    else()
        message(STATUS "Docs build disabled; verbose help embedding and manpage generation are skipped.")
    endif()
endif()

# Doxygen-based API docs
if(YAMS_BUILD_API_DOCS)
    find_package(Doxygen QUIET)
    if(DOXYGEN_FOUND)
        set(DOXYGEN_OUTPUT_DIR "${CMAKE_BINARY_DIR}/docs/api")
        file(MAKE_DIRECTORY "${DOXYGEN_OUTPUT_DIR}")
        set(DOXYFILE_IN "${CMAKE_BINARY_DIR}/cmake/Doxyfile.in")
        set(DOXYFILE_OUT "${CMAKE_BINARY_DIR}/Doxyfile")
        file(WRITE "${DOXYFILE_IN}"
"PROJECT_NAME = \"YAMS\"
OUTPUT_DIRECTORY = \"${DOXYGEN_OUTPUT_DIR}\"
GENERATE_HTML = YES
GENERATE_LATEX = NO
QUIET = YES
WARN_AS_ERROR = NO
INPUT = \"${CMAKE_SOURCE_DIR}/include\"
RECURSIVE = YES
EXTRACT_PRIVATE = NO
")
        configure_file("${DOXYFILE_IN}" "${DOXYFILE_OUT}" @ONLY)
        add_custom_target(docs-api
            COMMAND "${DOXYGEN_EXECUTABLE}" "${DOXYFILE_OUT}"
            WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
            COMMENT "Generating API documentation with Doxygen"
            VERBATIM
        )
    else()
        message(WARNING "YAMS_BUILD_API_DOCS=ON but Doxygen not found. Install doxygen to enable API docs.")
    endif()
endif()

# Configure based on build profile
if(YAMS_BUILD_PROFILE STREQUAL "release")
    message(STATUS "Using RELEASE build profile - CLI and MCP server only")
    set(YAMS_BUILD_CLI ON CACHE BOOL "" FORCE)
    set(YAMS_BUILD_MCP_SERVER ON CACHE BOOL "" FORCE)
    set(YAMS_BUILD_MAINTENANCE_TOOLS OFF CACHE BOOL "" FORCE)
    set(YAMS_BUILD_TESTS OFF CACHE BOOL "" FORCE)
    set(YAMS_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE)
    set(YAMS_BUILD_STRESS_TESTS OFF CACHE BOOL "" FORCE)
    if(NOT CMAKE_BUILD_TYPE)
        set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
    endif()
elseif(YAMS_BUILD_PROFILE STREQUAL "dev")
    message(STATUS "Using DEVELOPMENT build profile - all components")
    set(YAMS_BUILD_CLI ON CACHE BOOL "" FORCE)
    set(YAMS_BUILD_MCP_SERVER ON CACHE BOOL "" FORCE)
    set(YAMS_BUILD_MAINTENANCE_TOOLS ON CACHE BOOL "" FORCE)
    set(YAMS_BUILD_TESTS ON CACHE BOOL "" FORCE)
    set(YAMS_BUILD_BENCHMARKS ON CACHE BOOL "" FORCE)
    if(NOT CMAKE_BUILD_TYPE)
        set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE)
    endif()
else()
    message(STATUS "Using CUSTOM build profile - individual options apply")
    # Set a sensible default build type for single-config generators when unset
    if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
        set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
        set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo MinSizeRel)
    endif()
endif()

# Legacy compatibility
set(YAMS_BUILD_TOOLS ${YAMS_BUILD_CLI})

# C++ feature detection
include(CheckCXXSourceCompiles)

if(NOT YAMS_CXX17_MODE)
    # C++20 feature checks (only when in C++20 mode)
    check_cxx_source_compiles("
        #include <concepts>
        template<typename T>
        concept Integral = std::integral<T>;
        int main() {
            return 0;
        }
    " YAMS_HAS_CXX20_CONCEPTS)
    
    # std::span check is already done above for compatibility layer
    
    if(NOT YAMS_HAS_CXX20_CONCEPTS)
        message(WARNING "C++20 concepts not available, some features may be limited")
        add_compile_definitions(YAMS_HAS_CXX20_CONCEPTS=0)
    else()
        add_compile_definitions(YAMS_HAS_CXX20_CONCEPTS=1)
        message(STATUS "C++20 concepts available")
    endif()
else()
    # C++17 mode - set feature flags appropriately
    add_compile_definitions(YAMS_HAS_CXX20_CONCEPTS=0)
    message(STATUS "C++17 mode: C++20 concepts not available")
endif()

# Compiler warnings
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
    add_compile_options(
        -Wall -Wextra -Wpedantic
        -Wcast-align -Wcast-qual
        -Wdouble-promotion -Wformat=2
        -Wnon-virtual-dtor -Wshadow
        $<$<CONFIG:Debug>:-Og>
        $<$<CONFIG:Debug>:-g3>
        $<$<CONFIG:Release>:-O3>
    )

    # Architecture-specific optimizations
    if(APPLE AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64")
        # Apple Silicon (M1/M2/M3) optimizations
        if(YAMS_ENABLE_APPLE_SILICON_OPTIMIZATIONS)
            add_compile_options($<$<CONFIG:Release>:-mcpu=apple-m1>)
            add_compile_options($<$<CONFIG:Release>:-fvectorize>)
            message(STATUS "Apple Silicon optimizations enabled (-mcpu=apple-m1)")
        endif()
    elseif(YAMS_ENABLE_NATIVE_OPTIMIZATIONS)
        # x86_64 and other architectures
        add_compile_options($<$<CONFIG:Release>:-march=native>)
        if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")
            add_compile_options($<$<CONFIG:Release>:-mtune=native>)
        endif()
        message(STATUS "Native optimizations enabled (-march=native)")
    endif()

    # Coroutine support
    if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
        add_compile_options(-fcoroutines)
    elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
        if(APPLE OR YAMS_CLANG_USE_LIBCXX)
            add_compile_options(-stdlib=libc++)
            add_link_options(-stdlib=libc++)
        endif()
        # Apple Clang has coroutines enabled by default in C++20
        if(NOT APPLE)
            add_compile_options(-fcoroutines-ts)
        endif()
    endif()

    # Optional coroutine capability probe (warn-only, runs after flags above)
    check_cxx_source_compiles("
        #include <coroutine>
        struct task {
            struct promise_type {
                task get_return_object() { return {}; }
                std::suspend_never initial_suspend() noexcept { return {}; }
                std::suspend_never final_suspend() noexcept { return {}; }
                void return_void() noexcept {}
                void unhandled_exception() {}
            };
        };
        task foo() { co_return; }
        int main() { foo(); return 0; }
    " HAVE_CXX20_COROUTINES)

    if(NOT HAVE_CXX20_COROUTINES)
        message(WARNING "C++20 coroutines appear unsupported with current flags; features requiring <coroutine> may fail to build.")
    endif()
elseif(MSVC)
    add_compile_options(
        /W4 /permissive- /Zc:__cplusplus
        /Zc:preprocessor  # For proper __VA_OPT__ support
        $<$<CONFIG:Debug>:/Od /RTC1>
        $<$<CONFIG:Release>:/O2 /GL>
    )
    add_link_options($<$<CONFIG:Release>:/LTCG>)
endif()

# Sanitizers
if(YAMS_ENABLE_SANITIZERS AND CMAKE_BUILD_TYPE STREQUAL "Debug")
    if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
        add_compile_options(-fsanitize=address,undefined -fno-omit-frame-pointer)
        add_link_options(-fsanitize=address,undefined)
    endif()
endif()

# Code Coverage
if(YAMS_ENABLE_COVERAGE)
    if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
        add_compile_options(-fprofile-arcs -ftest-coverage)
        add_link_options(-fprofile-arcs -ftest-coverage)
        set(COVERAGE_LIBRARIES gcov)
    elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
        add_compile_options(-fprofile-instr-generate -fcoverage-mapping)
        add_link_options(-fprofile-instr-generate -fcoverage-mapping)
    endif()

    # Find coverage tools
    find_program(GCOVR_PATH gcovr)
    find_program(LCOV_PATH lcov)
    find_program(GENHTML_PATH genhtml)

    if(GCOVR_PATH)
        message(STATUS "Found gcovr: ${GCOVR_PATH}")
    elseif(LCOV_PATH AND GENHTML_PATH)
        message(STATUS "Found lcov: ${LCOV_PATH}")
        message(STATUS "Found genhtml: ${GENHTML_PATH}")
    else()
        message(WARNING "No coverage report generator found. Install gcovr or lcov for coverage reports.")
    endif()

    message(STATUS "Code coverage enabled")
endif()

# Dependencies
include(FetchContent)
set(FETCHCONTENT_QUIET FALSE)

# OpenSSL
if(APPLE)
    # Help CMake find Homebrew's OpenSSL
    set(OPENSSL_ROOT_DIR ${OPENSSL_ROOT_DIR} /opt/homebrew/opt/openssl@3 /usr/local/opt/openssl@3)
endif()
find_package(OpenSSL REQUIRED)
message(STATUS "Found OpenSSL: ${OPENSSL_VERSION}")
message(STATUS "OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}")
message(STATUS "OpenSSL crypto library: ${OPENSSL_CRYPTO_LIBRARY}")

# Threads (required for websocketpp)
find_package(Threads REQUIRED)

# Ncurses detection for Linux builds
if(YAMS_BUILD_CLI AND NOT YAMS_USE_CONAN)
    if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
        # Look for ncurses with wide character support first
        find_package(Curses)
        if(NOT CURSES_FOUND)
            # Try pkg-config as fallback
            find_package(PkgConfig)
            if(PKG_CONFIG_FOUND)
                pkg_check_modules(NCURSES ncursesw)
                if(NOT NCURSES_FOUND)
                    pkg_check_modules(NCURSES ncurses)
                endif()
            endif()

            if(NOT NCURSES_FOUND)
                message(FATAL_ERROR
                    "ncurses library not found. Please install it:\n"
                    "  Ubuntu/Debian: sudo apt-get install libncurses-dev\n"
                    "  RHEL/CentOS: sudo yum install ncurses-devel\n"
                    "  Fedora: sudo dnf install ncurses-devel\n"
                    "  Arch: sudo pacman -S ncurses")
            endif()
        else()
            message(STATUS "Found Curses: ${CURSES_LIBRARIES}")
        endif()
    endif()
endif()

# Google Test
if(YAMS_BUILD_TESTS)
    FetchContent_Declare(
        googletest
        GIT_REPOSITORY https://github.com/google/googletest.git
        GIT_TAG v1.14.0
    )
    set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
    FetchContent_MakeAvailable(googletest)
endif()

# Google Benchmark
# Handle benchmarks - only use FetchContent when not using Conan
if(YAMS_BUILD_BENCHMARKS AND NOT YAMS_USE_CONAN)
    FetchContent_Declare(
        benchmark
        GIT_REPOSITORY https://github.com/google/benchmark.git
        GIT_TAG v1.8.3
    )
    set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)

    # Disable werror for benchmark library to avoid strict warning issues
    set(BENCHMARK_ENABLE_WERROR OFF CACHE BOOL "" FORCE)

    FetchContent_MakeAvailable(benchmark)

    # Apply warning suppressions to benchmark target after it's created
    if(TARGET benchmark)
        if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
            target_compile_options(benchmark PRIVATE
                -Wno-sign-conversion
                -Wno-format-nonliteral
                -Wno-double-promotion
                -Wno-conversion
                -Wno-old-style-cast)
        endif()
    endif()

    if(TARGET benchmark_main)
        if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
            target_compile_options(benchmark_main PRIVATE
                -Wno-sign-conversion
                -Wno-format-nonliteral
                -Wno-double-promotion
                -Wno-conversion
                -Wno-old-style-cast)
        endif()
    endif()
endif()

# Dependency management
if(YAMS_USE_CONAN)
    # When using Conan, dependencies are provided via find_package
    find_package(spdlog REQUIRED)
    find_package(nlohmann_json REQUIRED)
    find_package(CLI11 REQUIRED)
    find_package(ZLIB REQUIRED)
    find_package(zstd REQUIRED)
    find_package(lz4 REQUIRED)
    find_package(SQLite3 REQUIRED)
    find_package(Protobuf REQUIRED)
    find_package(OpenSSL REQUIRED)
    # fmt for std::format fallback
    if(NOT YAMS_HAS_STD_FORMAT)
        find_package(fmt REQUIRED)
    endif()
    if(YAMS_BUILD_CLI)
        find_package(Curses REQUIRED)
    endif()
    if(YAMS_BUILD_MCP_SERVER)
        find_package(Drogon REQUIRED)
        # Boost will be handled via FetchContent below for non-Conan builds
    endif()
    if(YAMS_BUILD_TESTS)
        find_package(gtest REQUIRED)
        message(STATUS "Conan build: Found gtest package")
        if(TARGET gtest::gtest)
            message(STATUS "Conan build: gtest::gtest target is available")
        else()
            message(WARNING "Conan build: gtest::gtest target NOT found")
        endif()
    endif()
    if(YAMS_BUILD_BENCHMARKS)
        find_package(benchmark REQUIRED)
    endif()
endif()

# When not using Conan, use FetchContent for dependencies
message(STATUS "YAMS_USE_CONAN is: ${YAMS_USE_CONAN}")
if(NOT YAMS_USE_CONAN)
    message(STATUS "Using FetchContent for dependencies (non-Conan mode)")
    
    # fmt for std::format fallback when not available
    if(NOT YAMS_HAS_STD_FORMAT)
        FetchContent_Declare(
            fmt
            GIT_REPOSITORY https://github.com/fmtlib/fmt.git
            GIT_TAG 10.2.1
        )
        set(FMT_INSTALL OFF CACHE BOOL "" FORCE)
        FetchContent_MakeAvailable(fmt)
        message(STATUS "Using fmt library as std::format fallback")
    endif()
    
    # spdlog for logging
    FetchContent_Declare(
        spdlog
        GIT_REPOSITORY https://github.com/gabime/spdlog.git
        GIT_TAG v1.12.0
    )
    # Configure spdlog for proper installation
    # Enable installation so spdlog can be exported with YAMS targets
    set(SPDLOG_INSTALL ON CACHE BOOL "" FORCE)
    set(SPDLOG_BUILD_SHARED OFF CACHE BOOL "" FORCE)
    set(SPDLOG_FMT_EXTERNAL OFF CACHE BOOL "" FORCE)
    FetchContent_MakeAvailable(spdlog)

    # nlohmann_json for JSON parsing
    find_package(nlohmann_json 3.11 QUIET)
    if(NOT nlohmann_json_FOUND)
        FetchContent_Declare(
            nlohmann_json
            GIT_REPOSITORY https://github.com/nlohmann/json.git
            GIT_TAG v3.11.3
        )
        FetchContent_MakeAvailable(nlohmann_json)

        # Export nlohmann_json with YamsTargets for downstream consumers
        install(TARGETS nlohmann_json
            EXPORT YamsTargets
        )
    endif()

    # Install nlohmann_json headers (it's header-only)
    install(DIRECTORY ${nlohmann_json_SOURCE_DIR}/include/
        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
    )

    # Boost for MCP server (Beast/ASIO for WebSocket support)
    if(YAMS_BUILD_MCP_SERVER)
        message(STATUS "Fetching Boost headers for MCP server")
        FetchContent_Declare(
            Boost
            URL https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.xz
            URL_HASH SHA256=2e64e5d79a738d0fa6fb546c6e5c2bd28f88d268a2a080546f74e5ff98f29d0e
            DOWNLOAD_EXTRACT_TIMESTAMP ON
        )
        FetchContent_MakeAvailable(Boost)

        # Boost automatically creates boost_headers target, just need to make it available
        # The Boost::headers alias should already exist from the Boost CMake configuration
        if(NOT TARGET Boost::headers)
            # Only create alias if it doesn't exist
            if(TARGET boost_headers)
                add_library(Boost::headers ALIAS boost_headers)
            else()
                # Fallback: create our own interface library
                add_library(yams_boost_headers INTERFACE)
                target_include_directories(yams_boost_headers SYSTEM INTERFACE ${boost_SOURCE_DIR})
                add_library(Boost::headers ALIAS yams_boost_headers)
            endif()
        endif()
    endif()
endif()

# CLI11 for command line parsing
if(YAMS_BUILD_TOOLS)
    if(NOT YAMS_USE_CONAN)
        FetchContent_Declare(
            CLI11
            GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git
            GIT_TAG v2.4.1
        )
        FetchContent_MakeAvailable(CLI11)
    endif()

    # ImTUI for Terminal User Interface (Immediate Mode TUI)
    if(YAMS_BUILD_CLI)
        if(YAMS_USE_CONAN)
            # Use FetchContent for ImTUI with Conan as well, and provide a stable wrapper target
            find_package(Curses REQUIRED)
            # Workaround: imtui's CMake checks tinfo when CURSES_NCURSES_HAS_NODELAY is false,
            # which can fail with CURSES_EXTRA_LIBRARY=NOTFOUND on some Conan/system combos.
            # Seed the result to skip the tinfo probe; we still link Curses::Curses explicitly.
            set(CURSES_NCURSES_HAS_NODELAY TRUE CACHE BOOL "Assume ncurses provides nodelay; avoid tinfo probe" FORCE)

            FetchContent_Declare(
                imtui
                GIT_REPOSITORY https://github.com/ggerganov/imtui.git
                GIT_TAG master
                GIT_SHALLOW TRUE
                GIT_SUBMODULES_RECURSE TRUE
            )
            set(IMTUI_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
            FetchContent_MakeAvailable(imtui)

            # Ensure consistent target name and link against Curses
            if(TARGET imtui-ncurses)
                target_link_libraries(imtui-ncurses INTERFACE Curses::Curses)
            elseif(TARGET imtui::imtui-ncurses)
                add_library(imtui-ncurses INTERFACE)
                target_link_libraries(imtui-ncurses INTERFACE imtui::imtui-ncurses Curses::Curses)
            else()
                message(FATAL_ERROR "ImTUI target 'imtui-ncurses' not found after FetchContent.")
            endif()

            # Platform-specific link adjustments for ImTUI
            if(APPLE)
                target_link_libraries(imtui-ncurses INTERFACE "-framework System")
            endif()
            if(UNIX AND NOT APPLE)
                # Link to tinfo if it exists as a separate library (common on Linux)
                find_library(TINFO_LIBRARY NAMES tinfo tinfow)
                if(TINFO_LIBRARY)
                    target_link_libraries(imtui-ncurses INTERFACE "${TINFO_LIBRARY}")
                endif()
            endif()
        else()
            # Use FetchContent for non-Conan builds
            FetchContent_Declare(
                imtui
                GIT_REPOSITORY https://github.com/ggerganov/imtui.git
                GIT_TAG master
                GIT_SHALLOW TRUE
                GIT_SUBMODULES_RECURSE TRUE
            )
            set(IMTUI_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
            FetchContent_MakeAvailable(imtui)
            # Ensure the wrapper target exists and link ncurses and tinfo as needed
            if(TARGET imtui-ncurses)
                if(TARGET Curses::Curses)
                    target_link_libraries(imtui-ncurses INTERFACE Curses::Curses)
                elseif(DEFINED CURSES_LIBRARIES)
                    target_link_libraries(imtui-ncurses INTERFACE ${CURSES_LIBRARIES})
                endif()
                if(UNIX AND NOT APPLE)
                    find_library(TINFO_LIBRARY NAMES tinfo tinfow)
                    if(TINFO_LIBRARY)
                        target_link_libraries(imtui-ncurses INTERFACE "${TINFO_LIBRARY}")
                    endif()
                endif()
            endif()
        endif()
    endif()
endif()

# Boost.Beast is part of Boost and will be available automatically
# No separate dependency needed for WebSocket transport

# Zstandard for compression - only if not using Conan
if(NOT YAMS_USE_CONAN)
    FetchContent_Declare(
        zstd
        URL https://github.com/facebook/zstd/releases/download/v1.5.6/zstd-1.5.6.tar.gz
        URL_HASH SHA256=8c29e06cf42aacc1eafc4077ae2ec6c6fcb96a626157e0593d5e82a34fd403c1
        SOURCE_SUBDIR build/cmake
    )
    set(ZSTD_BUILD_PROGRAMS OFF CACHE BOOL "" FORCE)
    set(ZSTD_BUILD_TESTS OFF CACHE BOOL "" FORCE)
    set(ZSTD_BUILD_SHARED OFF CACHE BOOL "" FORCE)
    set(ZSTD_BUILD_STATIC ON CACHE BOOL "" FORCE)
    FetchContent_MakeAvailable(zstd)
endif()

# LZMA SDK for compression - always needed since no good Conan package exists
# if(NOT YAMS_USE_CONAN)
FetchContent_Declare(
        lzma
        URL https://github.com/ip7z/7zip/releases/download/24.08/7z2408-src.tar.xz
        URL_HASH SHA256=aa04aac906a04df59e7301f4c69e9f48808e6c8ecae4eb697703a47bfb0ac042
        DOWNLOAD_EXTRACT_TIMESTAMP TRUE
    )
FetchContent_GetProperties(lzma)
if(NOT lzma_POPULATED)
    FetchContent_Populate(lzma)

    # Create LZMA library from SDK sources
    add_library(lzma_sdk STATIC
            ${lzma_SOURCE_DIR}/C/LzmaEnc.c
            ${lzma_SOURCE_DIR}/C/LzmaDec.c
            ${lzma_SOURCE_DIR}/C/LzFind.c
            ${lzma_SOURCE_DIR}/C/Lzma2Enc.c
            ${lzma_SOURCE_DIR}/C/Lzma2Dec.c
            ${lzma_SOURCE_DIR}/C/Lzma86Enc.c
            ${lzma_SOURCE_DIR}/C/Lzma86Dec.c
            ${lzma_SOURCE_DIR}/C/CpuArch.c
            ${lzma_SOURCE_DIR}/C/Alloc.c
            ${lzma_SOURCE_DIR}/C/LzFindMt.c
            ${lzma_SOURCE_DIR}/C/Threads.c
            ${lzma_SOURCE_DIR}/C/MtCoder.c
            ${lzma_SOURCE_DIR}/C/MtDec.c
            ${lzma_SOURCE_DIR}/C/Lzma2DecMt.c
            ${lzma_SOURCE_DIR}/C/LzFindOpt.c
            ${lzma_SOURCE_DIR}/C/7zStream.c
        )

    target_include_directories(lzma_sdk PUBLIC
            $<BUILD_INTERFACE:${lzma_SOURCE_DIR}/C>
            $<INSTALL_INTERFACE:include/lzma>
        )
    # Enable multi-threaded support for LZMA SDK

    # Disable warnings for third-party code
    if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
        target_compile_options(lzma_sdk PRIVATE -w)
    elseif(MSVC)
        target_compile_options(lzma_sdk PRIVATE /w)
    endif()
endif()
# endif()

# SQLite3 with FTS5 - only if not using Conan
if(NOT YAMS_USE_CONAN)
    FetchContent_Declare(
        sqlite3
        URL https://www.sqlite.org/2024/sqlite-amalgamation-3460100.zip
        URL_HASH SHA256=77823cb110929c2bcb0f5d48e4833b5c59a8a6e40cdea3936b99e199dbbe5784
        DOWNLOAD_EXTRACT_TIMESTAMP TRUE
    )
    FetchContent_GetProperties(sqlite3)
    if(NOT sqlite3_POPULATED)
        FetchContent_Populate(sqlite3)

        # Create SQLite library with FTS5 enabled
        add_library(sqlite3 STATIC
            ${sqlite3_SOURCE_DIR}/sqlite3.c
        )

        target_include_directories(sqlite3 PUBLIC
            $<BUILD_INTERFACE:${sqlite3_SOURCE_DIR}>
            $<INSTALL_INTERFACE:include/sqlite3>
        )

        # Enable FTS5 and other useful extensions
        target_compile_definitions(sqlite3 PRIVATE
            SQLITE_ENABLE_FTS5=1
            SQLITE_ENABLE_RTREE=1
            SQLITE_ENABLE_JSON1=1
            SQLITE_ENABLE_STAT4=1
            SQLITE_ENABLE_UPDATE_DELETE_LIMIT=1
            SQLITE_ENABLE_COLUMN_METADATA=1
            SQLITE_DEFAULT_MEMSTATUS=0
            SQLITE_DEFAULT_WAL_SYNCHRONOUS=1
            SQLITE_LIKE_DOESNT_MATCH_BLOBS=1
            SQLITE_MAX_EXPR_DEPTH=0
            SQLITE_OMIT_DEPRECATED=1
            SQLITE_OMIT_SHARED_CACHE=1
            SQLITE_USE_ALLOCA=1
            SQLITE_THREADSAFE=2
        )

        # Disable warnings for third-party code
        if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
            target_compile_options(sqlite3 PRIVATE -w)
        elseif(MSVC)
            target_compile_options(sqlite3 PRIVATE /w)
        endif()
    endif()
endif()

# PDFium for PDF text extraction
option(YAMS_ENABLE_PDF "Enable PDF text extraction support" ON)
# macOS runtime search path defaults for installed binaries
if(APPLE)
    set(CMAKE_MACOSX_RPATH ON)
    # Ensure installed executables/DYLIBs can find dependencies in lib/
    set(CMAKE_INSTALL_RPATH "@loader_path/../lib")
endif()

if(YAMS_ENABLE_PDF)
    message(STATUS "Configuring PDFium for PDF support")
    
    # Try to find PDFium from Conan first if using Conan
    set(PDFium_FOUND FALSE)
    if(YAMS_USE_CONAN)
        message(STATUS "Attempting to find PDFium via Conan...")
        find_package(PDFium QUIET)
        if(PDFium_FOUND)
            message(STATUS "PDFium found via Conan")
            add_library(pdfium::pdfium ALIAS PDFium::PDFium)
        else()
            message(STATUS "PDFium not available via Conan, falling back to FetchContent")
        endif()
    endif()
    
    # If PDFium not found via Conan, use FetchContent (both Conan and non-Conan builds)
    if(NOT PDFium_FOUND)
        # Determine platform and architecture
        if(APPLE)
            if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64")
                set(PDFIUM_PLATFORM "mac-arm64")
            else()
                set(PDFIUM_PLATFORM "mac-x64")
            endif()
        elseif(UNIX)
            if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
                set(PDFIUM_PLATFORM "linux-arm64")
            else()
                set(PDFIUM_PLATFORM "linux-x64")
            endif()
        elseif(WIN32)
            if(CMAKE_SIZEOF_VOID_P EQUAL 8)
                set(PDFIUM_PLATFORM "win-x64")
            else()
                set(PDFIUM_PLATFORM "win-x86")
            endif()
        endif()

        # Use prebuilt binaries from pdfium-binaries project
        # Latest release: PDFium 141.0.7350.0 (chromium/7350)
        FetchContent_Declare(
            pdfium
            URL https://github.com/bblanchon/pdfium-binaries/releases/download/chromium%2F7350/pdfium-${PDFIUM_PLATFORM}.tgz
            DOWNLOAD_EXTRACT_TIMESTAMP TRUE
        )

        FetchContent_GetProperties(pdfium)
        if(NOT pdfium_POPULATED)
        message(STATUS "Downloading PDFium binaries for ${PDFIUM_PLATFORM}...")
        FetchContent_Populate(pdfium)

        # Verify PDFium was downloaded correctly
        if(NOT EXISTS "${pdfium_SOURCE_DIR}/include/fpdfview.h")
            message(WARNING "PDFium download failed or incomplete. PDF support will be disabled.")
            set(YAMS_ENABLE_PDF OFF)
            return()
        endif()

        # Create imported target for PDFium
        add_library(pdfium SHARED IMPORTED GLOBAL)

        # Set include directories
        set_target_properties(pdfium PROPERTIES
            INTERFACE_INCLUDE_DIRECTORIES "${pdfium_SOURCE_DIR}/include"
        )

        # Set library location based on platform
        if(APPLE)
            set_target_properties(pdfium PROPERTIES
                IMPORTED_LOCATION "${pdfium_SOURCE_DIR}/lib/libpdfium.dylib"
            )
            # Ensure @rpath install_name on the fetched dylib for proper link-time embedding
            execute_process(COMMAND install_name_tool -id @rpath/libpdfium.dylib "${pdfium_SOURCE_DIR}/lib/libpdfium.dylib")
        elseif(UNIX)
            set_target_properties(pdfium PROPERTIES
                IMPORTED_LOCATION "${pdfium_SOURCE_DIR}/lib/libpdfium.so"
            )
        elseif(WIN32)
            set_target_properties(pdfium PROPERTIES
                IMPORTED_LOCATION "${pdfium_SOURCE_DIR}/bin/pdfium.dll"
                IMPORTED_IMPLIB "${pdfium_SOURCE_DIR}/lib/pdfium.lib"
            )
        endif()

        # Create alias for consistent naming
        add_library(pdfium::pdfium ALIAS pdfium)

        # Install the prebuilt PDFium library based on platform
        if(APPLE)
            install(FILES "${pdfium_SOURCE_DIR}/lib/libpdfium.dylib"
                    DESTINATION ${CMAKE_INSTALL_LIBDIR}
                    COMPONENT runtime)
        elseif(UNIX)
            install(FILES "${pdfium_SOURCE_DIR}/lib/libpdfium.so"
                    DESTINATION ${CMAKE_INSTALL_LIBDIR}
                    COMPONENT runtime)
        elseif(WIN32)
            install(FILES "${pdfium_SOURCE_DIR}/bin/pdfium.dll"
                    DESTINATION ${CMAKE_INSTALL_BINDIR}
                    COMPONENT runtime)
            install(FILES "${pdfium_SOURCE_DIR}/lib/pdfium.lib"
                    DESTINATION ${CMAKE_INSTALL_LIBDIR}
                    COMPONENT development)
        endif()

        if(APPLE)
            # Ensure consumers load PDFium via @rpath; installed binaries use @loader_path/../lib
            install(CODE "execute_process(COMMAND install_name_tool -id @rpath/libpdfium.dylib \"${CMAKE_INSTALL_FULL_LIBDIR}/libpdfium.dylib\")")

            # Patch installed executables to reference @rpath/libpdfium.dylib and ensure rpath includes ../lib
            install(CODE "
                set(_bins yams yams-mcp-server)
                foreach(_bin IN LISTS _bins)
                    set(_path \"${CMAKE_INSTALL_FULL_BINDIR}/${_bin}\")
                    if(EXISTS \"${_path}\")
                        execute_process(COMMAND install_name_tool -change libpdfium.dylib @rpath/libpdfium.dylib \"${_path}\")
                        execute_process(COMMAND install_name_tool -change ./libpdfium.dylib @rpath/libpdfium.dylib \"${_path}\")
                        execute_process(COMMAND install_name_tool -add_rpath \"@loader_path/../lib\" \"${_path}\")
                    endif()
                endforeach()
            ")
        endif()

        message(STATUS "PDFium ${PDFIUM_PLATFORM} successfully configured")
        set(YAMS_PDFIUM_FOUND TRUE CACHE BOOL "PDFium library found and configured")
        endif()  # end if(NOT pdfium_POPULATED)
    endif()  # end if(NOT PDFium_FOUND)
else()
    message(STATUS "PDFium support disabled (YAMS_ENABLE_PDF=OFF)")
    set(YAMS_PDFIUM_FOUND FALSE CACHE BOOL "PDFium library found and configured")
endif()

# Protocol Buffers (optional, for manifest serialization)
find_package(Protobuf QUIET)
if(Protobuf_FOUND)
    message(STATUS "Found Protobuf: ${Protobuf_VERSION}")

    # Find Abseil (required by modern Protocol Buffers)
    find_package(absl QUIET)
    if(absl_FOUND)
        message(STATUS "Found Abseil for Protocol Buffers")
    else()
        # Try to find Abseil through pkg-config
        find_package(PkgConfig QUIET)
        if(PkgConfig_FOUND)
            pkg_check_modules(ABSL absl_base absl_strings absl_log absl_log_internal)
            if(ABSL_FOUND)
                message(STATUS "Found Abseil via pkg-config")
            endif()
        endif()
    endif()
else()
    message(STATUS "Protobuf not found, using custom serialization")
endif()

# Create core library
add_library(yams_core INTERFACE)
target_include_directories(yams_core INTERFACE
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)
if(YAMS_CXX17_MODE)
    target_compile_features(yams_core INTERFACE cxx_std_17)
else()
    target_compile_features(yams_core INTERFACE cxx_std_20)
endif()
add_library(yams::core ALIAS yams_core)

# Add subdirectories
add_subdirectory(src)

if(YAMS_BUILD_TESTS)
    enable_testing()
    message(STATUS "Building tests: YAMS_USE_CONAN=${YAMS_USE_CONAN}")

    # Use FetchContent for GTest only when not using Conan
    if(NOT YAMS_USE_CONAN)
        message(STATUS "Non-Conan build: Using FetchContent for GoogleTest")
        include(FetchContent)
        FetchContent_Declare(
            googletest
            GIT_REPOSITORY https://github.com/google/googletest.git
            GIT_TAG v1.14.0
            GIT_SHALLOW TRUE
        )
        # Prevent overriding the parent project's compiler/linker settings on Windows
        set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
        FetchContent_MakeAvailable(googletest)
        
        # Create alias targets for compatibility with find_package(GTest)
        if(TARGET gtest AND NOT TARGET GTest::gtest)
            add_library(GTest::gtest ALIAS gtest)
        endif()
        if(TARGET gtest_main AND NOT TARGET GTest::gtest_main)
            add_library(GTest::gtest_main ALIAS gtest_main)
        endif()
        if(TARGET gmock AND NOT TARGET GTest::gmock)
            add_library(GTest::gmock ALIAS gmock)
        endif()
        if(TARGET gmock_main AND NOT TARGET GTest::gmock_main)
            add_library(GTest::gmock_main ALIAS gmock_main)
        endif()
    endif()

    add_subdirectory(tests)
endif()

# CTest is included for test discovery
include(CTest)



# Build tools based on options
if(YAMS_BUILD_CLI OR YAMS_BUILD_MCP_SERVER OR YAMS_BUILD_MAINTENANCE_TOOLS)
    add_subdirectory(tools)
endif()

# Installation
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)

# ---------------------------------------------------------------------------
# RPATH best practices (for installed targets on macOS/Linux)
# ---------------------------------------------------------------------------
# Use build RPATH so binaries run from the build tree
set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)

# Add install directories to the install RPATH
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)

# macOS specific
if(APPLE)
    set(CMAKE_MACOSX_RPATH ON)
endif()

# If the install libdir isn't a system dir, add it to the install RPATH
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_LIBDIR}" _isSys)
if(_isSys EQUAL -1)
    set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
endif()

# ---------------------------------------------------------------------------
# Uninstall target (uses install_manifest.txt generated by "make install")
# ---------------------------------------------------------------------------
set(UNINSTALL_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
file(WRITE "${UNINSTALL_SCRIPT}" "if(NOT EXISTS \"${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt\")\n  message(FATAL_ERROR \"Cannot find install manifest: ${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt\")\nendif()\nfile(READ \"${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt\" _files)\nstring(REPLACE \"\\n\" \";\" _files \"${_files}\")\nforeach(_file IN LISTS _files)\n  if(_file STREQUAL \"\")\n    continue()\n  endif()\n  if(EXISTS \"${_file}\" OR IS_SYMLINK \"${_file}\")\n    file(REMOVE \"${_file}\")\n  endif()\nendforeach()\n")
if(NOT TARGET uninstall)
    add_custom_target(uninstall
        COMMAND ${CMAKE_COMMAND} -P "${UNINSTALL_SCRIPT}"
        COMMENT "Removing installed files listed in install_manifest.txt"
    )
endif()

# Package config (install location configurable)


# Export targets
set(YAMS_EXPORT_TARGETS yams_core yams_indexing)

# Always export lzma_sdk since we always build it
if(TARGET lzma_sdk)
    list(APPEND YAMS_EXPORT_TARGETS lzma_sdk)
endif()

# Only export sqlite3 if we built it (non-Conan)
if(NOT YAMS_USE_CONAN)
    if(TARGET sqlite3)
        list(APPEND YAMS_EXPORT_TARGETS sqlite3)
    endif()
endif()

install(TARGETS ${YAMS_EXPORT_TARGETS}
    EXPORT YamsTargets
)

install(EXPORT YamsTargets
    FILE YamsTargets.cmake
    NAMESPACE yams::
    DESTINATION ${YAMS_CMAKE_PACKAGE_DIR}
)

# Install headers
install(DIRECTORY include/
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
    COMPONENT development
)

# Install data files
install(FILES 
    data/magic_numbers.json
    DESTINATION ${CMAKE_INSTALL_DATADIR}/yams/data
    COMPONENT runtime
)

install(FILES
    sql/reference_schema.sql
    DESTINATION ${CMAKE_INSTALL_DATADIR}/yams/sql
    COMPONENT runtime
)

# CPack configuration for creating packages
set(CPACK_PACKAGE_NAME "YAMS")
set(CPACK_PACKAGE_VENDOR "YAMS Project")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Yet Another Memory System - High-performance content-addressed storage")
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")

# Components
set(CPACK_COMPONENTS_ALL runtime maintenance development)
set(CPACK_COMPONENT_RUNTIME_DISPLAY_NAME "Runtime")
set(CPACK_COMPONENT_RUNTIME_DESCRIPTION "YAMS CLI and MCP server")
set(CPACK_COMPONENT_MAINTENANCE_DISPLAY_NAME "Maintenance Tools")
set(CPACK_COMPONENT_MAINTENANCE_DESCRIPTION "Garbage collection and statistics tools")
set(CPACK_COMPONENT_DEVELOPMENT_DISPLAY_NAME "Development")
set(CPACK_COMPONENT_DEVELOPMENT_DESCRIPTION "Headers and libraries for development")

# Package generation
if(YAMS_BUILD_PROFILE STREQUAL "release")
    set(CPACK_COMPONENTS_ALL runtime)
endif()

include(CPack)

# Create package config
configure_package_config_file(
    "${CMAKE_CURRENT_SOURCE_DIR}/cmake/YamsConfig.cmake.in"
    "${CMAKE_CURRENT_BINARY_DIR}/YamsConfig.cmake"
    INSTALL_DESTINATION ${YAMS_CMAKE_PACKAGE_DIR}
)

write_basic_package_version_file(
    "${CMAKE_CURRENT_BINARY_DIR}/YamsConfigVersion.cmake"
    VERSION ${PROJECT_VERSION}
    COMPATIBILITY SameMajorVersion
)

install(FILES
    "${CMAKE_CURRENT_BINARY_DIR}/YamsConfig.cmake"
    "${CMAKE_CURRENT_BINARY_DIR}/YamsConfigVersion.cmake"
    DESTINATION ${YAMS_CMAKE_PACKAGE_DIR}
)

# Coverage targets
if(YAMS_ENABLE_COVERAGE)
    # Create coverage directory
    file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/coverage)

    # Coverage target using gcovr (preferred)
    if(GCOVR_PATH)
        add_custom_target(coverage-html
            COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/coverage/html
            COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/coverage/html
            COMMAND ${GCOVR_PATH} --root ${CMAKE_SOURCE_DIR}
                --exclude ${CMAKE_SOURCE_DIR}/tests/
                --exclude ${CMAKE_SOURCE_DIR}/_deps/
                --exclude ${CMAKE_SOURCE_DIR}/build/
                --html --html-details
                --output ${CMAKE_BINARY_DIR}/coverage/html/index.html
            WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
            COMMENT "Generating coverage HTML report with gcovr"
        )

        add_custom_target(coverage-xml
            COMMAND ${GCOVR_PATH} --root ${CMAKE_SOURCE_DIR}
                --exclude ${CMAKE_SOURCE_DIR}/tests/
                --exclude ${CMAKE_SOURCE_DIR}/_deps/
                --exclude ${CMAKE_SOURCE_DIR}/build/
                --xml --output ${CMAKE_BINARY_DIR}/coverage/coverage.xml
            WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
            COMMENT "Generating coverage XML report with gcovr"
        )

        add_custom_target(coverage-summary
            COMMAND ${GCOVR_PATH} --root ${CMAKE_SOURCE_DIR}
                --exclude ${CMAKE_SOURCE_DIR}/tests/
                --exclude ${CMAKE_SOURCE_DIR}/_deps/
                --exclude ${CMAKE_SOURCE_DIR}/build/
            WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
            COMMENT "Displaying coverage summary"
        )

        # Coverage target using lcov (alternative)
    elseif(LCOV_PATH AND GENHTML_PATH)
        add_custom_target(coverage-html
            COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/coverage/html
            COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/coverage/html
            COMMAND ${LCOV_PATH} --capture --directory ${CMAKE_BINARY_DIR}
                --output-file ${CMAKE_BINARY_DIR}/coverage/coverage.info
            COMMAND ${LCOV_PATH} --remove ${CMAKE_BINARY_DIR}/coverage/coverage.info
                '${CMAKE_SOURCE_DIR}/tests/*'
                '${CMAKE_SOURCE_DIR}/_deps/*'
                '${CMAKE_SOURCE_DIR}/build/*'
                '/usr/*' '/opt/*'
                --output-file ${CMAKE_BINARY_DIR}/coverage/coverage.info
            COMMAND ${GENHTML_PATH} ${CMAKE_BINARY_DIR}/coverage/coverage.info
                --output-directory ${CMAKE_BINARY_DIR}/coverage/html
            WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
            COMMENT "Generating coverage HTML report with lcov"
        )

        add_custom_target(coverage-summary
            COMMAND ${LCOV_PATH} --summary ${CMAKE_BINARY_DIR}/coverage/coverage.info
            WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
            COMMENT "Displaying coverage summary"
        )
    endif()

    # Combined coverage target that runs tests first
    if(TARGET coverage-html)
        add_custom_target(coverage
            COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
            COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
            COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target coverage-html
            WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
            COMMENT "Running tests and generating coverage report"
        )
    endif()
endif()

# CPack configuration
set(CPACK_PACKAGE_VENDOR "YAMS Project")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_DESCRIPTION})
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})

# CPack already included above at line 1306

# Generate and install CMake package config for find_package(Yams)
include(CMakePackageConfigHelpers)

configure_package_config_file(
    "${CMAKE_CURRENT_SOURCE_DIR}/cmake/YamsConfig.cmake.in"
    "${CMAKE_CURRENT_BINARY_DIR}/YamsConfig.cmake"
    INSTALL_DESTINATION ${YAMS_CMAKE_PACKAGE_DIR}
)

write_basic_package_version_file(
    "${CMAKE_CURRENT_BINARY_DIR}/YamsConfigVersion.cmake"
    VERSION ${PROJECT_VERSION}
    COMPATIBILITY SameMajorVersion
)

install(FILES
    "${CMAKE_CURRENT_BINARY_DIR}/YamsConfig.cmake"
    "${CMAKE_CURRENT_BINARY_DIR}/YamsConfigVersion.cmake"
    DESTINATION ${YAMS_CMAKE_PACKAGE_DIR}
)
